﻿using System;
using Nuwanda.Common;
using Nuwanda.TfsSpotlight.Data;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Nuwanda.TfsSpotlight.Services.Interfaces;
using Nuwanda.TfsSpotlight.Shell.Proxies.Interfaces;
using Nuwanda.TfsSpotlight.Shell.Services;
using Nuwanda.TfsSpotlight.Shell.Services.Interfaces;
using Nuwanda.TfsSpotlight.TFS;
using System.Collections.Generic;
using System.Windows.Forms;
using Nuwanda.TfsSpotlight.Views.WorkItems;
using StructureMap;

namespace Nuwanda.TfsSpotlight.Views.Query
{
    [Author("Bartłomiej Legiędź")]
    [Pluggable("QueryPresenter")]
    public class QueryPresenter : TabPresenterBase<IQueryView>, IQueryPresenter, IServerAssociation
    {
        private QueryState _queryState;
        protected ITfsServerManager _tfsServerManager;
        
        private IMonitService MonitService { get; set; }
        private ITabsService TabsService { get; set; }
        private IUserSettingsService UserSettingsService { get; set; }
        protected IEmbeddedWorkItemPresenter EmbeddedWorkItemPresenter { get; set; }
        private ITfsServerRepository TfsServerRepository { get; set; }

        public QueryDescriptor Query { get; internal set; }

        public int SelectedWorkItemId
        {
            get
            {
                var workItem = GetSelectedWorkItem();
                return workItem != null ? workItem.Id : -1;
            }
        }

        public QueryResult DataSource { get; private set; }

        public ServerDescriptor Server
        {
            get { return this.Query != null ? this.Query.Project.Server : null; }
        }

        public ProjectDescriptor Project
        {
            get { return this.Query != null ? this.Query.Project : null; }
        }

        public override bool IsDirty
        {
            get
            {
                if (this.DataSource == null || this.DataSource.Result == null) return false;

                foreach (WorkItem item in this.DataSource.Result)
                {
                    if (item.IsDirty) return true;
                }

                return false;
            }
        }

        public int WorkItemId
        {
            get { return this.SelectedWorkItemId; }
        }

        public QueryPresenter(IQueryView view,
                              IMonitService monitService, IMenuService menuService, 
                              ITabsService tabsService,IUserSettingsService userSettingsService,
                              IWorker worker, ILogger logger, ITfsServerRepository tfsServerRepository)
            : base(view, worker, menuService, logger)
        {
            this.MonitService = monitService;
            this.TabsService = tabsService;
            this.UserSettingsService = userSettingsService;
            this.TfsServerRepository = tfsServerRepository;
        }

        public void Initialize(string searchText, ProjectDescriptor project)
        {
            Initialize(QueryDescriptor.GetSearchQuery(searchText, project));
        }

        public virtual void Initialize(QueryDescriptor query)
        {
            this.View.AttachPresenter(this);

            this._tfsServerManager = this.TfsServerRepository.GetServer(query.Project.Server);
            this.EmbeddedWorkItemPresenter =
                StructureMap.ObjectFactory.With(this._tfsServerManager).GetInstance<IEmbeddedWorkItemPresenter>();

            this.View.FillDetailsForm(this.EmbeddedWorkItemPresenter.Control);
            this.EmbeddedWorkItemPresenter.Initialize(query.Project.Server, this);

            this.Query = query;
            this._queryState = QueryState.None;

            this.View.ApplayLayoutState(this.UserSettingsService.LoadQueryLayoutState(this.Query));
            this.View.EnableResultGrid();
            this.View.StopResultGridProgress();
            this.View.SetQueryCaption(String.Empty, String.Empty);
            this.View.HideDetails();

            this.MenuService.RegisterCommandActivator(CommandsNames.Refresh)
                .Activator(this.ActivateRefreshQueryCommand).Context(this).Commit();
            this.MenuService.RegisterCommandActivator(CommandsNames.GoToNextWorkItem)
                .Activator(this.ActivateGoToNextWorkItemCommand).Context(this).Commit();
            this.MenuService.RegisterCommandActivator(CommandsNames.GoToNextWorkItem)
                .Activator(this.ActivateGoToPrevoiousWorkItemCommand).Context(this).Commit();
            this.MenuService.RegisterCommandActivator(CommandsNames.SaveAllWorkItems)
                .Activator(this.ActivateSaveAllWorkItemsCommand).Context(this).Commit();
            this.MenuService.RegisterCommandActivator(CommandsNames.OpenChart)
                .Activator(this.ActivateOpenChartCommand).Context(this).Commit();
        }

        #region Command activators

        private bool ActivateGoToNextWorkItemCommand()
        {
            return DefaultCommandActivator() && this.View.CanMoveToNextWorkItem;
        }

        private bool ActivateGoToPrevoiousWorkItemCommand()
        {
            return DefaultCommandActivator() && this.View.CanMoveToPreviousWorkItem;
        }

        private bool ActivateRefreshQueryCommand()
        {
            return DefaultCommandActivator();
        }

        private bool ActivateSaveAllWorkItemsCommand()
        {
            return DefaultCommandActivator() && this.IsDirty;
        }

        private bool ActivateOpenChartCommand()
        {
            return DefaultCommandActivator();
        }

        public bool EmbeddedWorkItemCommandsAcivator()
        {
            return DefaultCommandActivator();
        }
        #endregion

        public override void HandleLoad()
        {
            HandleRefresh();
        }

        public override void HandleRefresh()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this._queryState = QueryState.Refreshing;
                this.Tab.RefreshCaption();
                this.View.SetQueryCaption(String.Format(Captions.BarQueryRunning, this.Query.Name), String.Empty);

                this.EmbeddedWorkItemPresenter.DisableView();
                
                this.View.DisableResultGrid();
                this.View.StartResultGridProgress();
                this.View.ClearResults();

                this.DataSource = this._tfsServerManager.RunQuery(this.Query);

                if (this.DataSource == null)
                {
                    this.MonitService.ShowStatusMessage(Messages.MsgInternalErrorWhileRunningQuery, MessageSeverity.Error);
                    return;
                }
                
                this.View.DisplayResult(this.DataSource);

                var resultCount = this.DataSource.Result == null ? 0 : this.DataSource.Result.Count;

                if (resultCount == 0)
                {
                    this.View.HideDetails();
                    this.EmbeddedWorkItemPresenter.HandleDeactivation();
                }

                this._queryState = QueryState.None;
                this.View.SetQueryCaption(
                    String.Format(Captions.BarQueryNormal, this.Query.Name),
                    String.Format(Captions.BarQueryItems, resultCount));
                this.Tab.RefreshCaption();
                this.View.SelectFirstQueryResult();
            }
            catch (CouldNotConnectToTfsServerException cne)
            {
                this.View.ShowError(cne.Message);
                this.View.SetQueryCaption(String.Format(Captions.BarQueryUnableToRefresh, this.Query.Name), String.Empty);
                this._queryState = QueryState.UnableToRefresh;
                this.Tab.RefreshCaption();
            }
            catch (QueryValidationException qve)
            {
                this.View.ShowError(qve.Message);
                this.View.SetQueryCaption(String.Format(Captions.BarQueryNormal, this.Query.Name), String.Empty);
                this._queryState = QueryState.None;
                this.Tab.RefreshCaption();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
            }
            finally
            {
                this.View.EnableResultGrid();
                this.EmbeddedWorkItemPresenter.EnableView();
                this.View.StopResultGridProgress();

                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        public void HandleSelectedWorkItemChanged()
        {
            this.MenuService.RunCommand(CommandsNames.LoadWorkItem);
        }

        public override void WaitTillTheEndOfTheWork()
        {
            base.WaitTillTheEndOfTheWork();
            this.EmbeddedWorkItemPresenter.WaitTillTheEndOfTheWork();
        }

        protected virtual WorkItem GetSelectedWorkItem()
        {
            var workItemIndex = this.View.GetSelectedWorkItemIndex();
            return workItemIndex == -1 ? null : this.DataSource.Result[workItemIndex];
        }

        public void HandleLoadWorkItem()
        {
            try
            {
                this.View.HideMessage();
                this.Worker.WaitAndBeginWork();
                this.InvalidateHandledCommands();

                var workItem = GetSelectedWorkItem();

                if (workItem == null)
                {
                    HideWorkItemDetails();
                    return;
                }

                this.EmbeddedWorkItemPresenter.HandleActivation();

                this.View.ShowDetails();
                this.EmbeddedWorkItemPresenter.HandleLoad(workItem);
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
                HideWorkItemDetails();
            }
            finally
            {
                this.View.UpdateResultGridRows();
                this.Tab.RefreshCaption();
                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        private void HideWorkItemDetails()
        {
            this.View.HideDetails();
            this.EmbeddedWorkItemPresenter.HandleDeactivation();
            this.MonitService.ClearStatusMessage();
        }

        public override void HandleSave()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this.View.ShowDetails();
                this.EmbeddedWorkItemPresenter.HandleSave();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
            }
            finally
            {
                this.View.UpdateResultGridRows();
                this.Tab.RefreshCaption();
                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        public void HandleRefreshWorkItem()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this.View.ShowDetails();
                this.EmbeddedWorkItemPresenter.HandleRefresh();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
            }
            finally
            {
                this.View.UpdateResultGridRows();
                this.Tab.RefreshCaption();
                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        public void HandleSelectNextWorkItem()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this.View.SelectNextWorkItem();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
            }
            finally
            {
                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        public void HandleSelectPreviousWorkItem()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this.View.SelectPrevoiusWorkItem();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
            }
            finally
            {
                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        public void HandleSaveAll()
        {
            if (!this.Worker.BeginWork()) return;
            this.InvalidateHandledCommands();

            try
            {
                this.View.HideMessage();
                this.EmbeddedWorkItemPresenter.DisableView();
                this.View.DisableResultGrid();
                this.SaveAll();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
                this.Logger.WriteException(e);
            }
            finally
            {
                this.View.UpdateResultGridRows();
                this.EmbeddedWorkItemPresenter.EnableView();
                this.View.EnableResultGrid();
                this.Tab.RefreshCaption();

                this.Worker.EndWork();
                this.InvalidateHandledCommands();
            }
        }

        private void SaveAll()
        {
            var dirtyWorkItems = new List<WorkItem>();

            foreach (WorkItem item in this.DataSource.Result)
            {
                if (item.IsDirty)
                    dirtyWorkItems.Add(item);
            }

            this._tfsServerManager.SaveWorkItems(dirtyWorkItems);
        }

        public override void HandleClose()
        {
            //BUG: Return true if closed, and TabService must take it into account!
            //     Close tab while working by using X on the right!
            if (!this.Worker.BeginWork()) return;

            try
            {
                if (this.IsDirty)
                {
                    var result = this.MonitService.ShowQuestion(
                        String.Format(Messages.QuestionSaveEditedWorkItemsWhileClosingQuery, this.GetCaption()),
                        MessageBoxButtons.YesNo);

                    if (result == DialogResult.Yes)
                    {
                        this.SaveAll();
                    }
                }

                this.SaveLayoutState();
            }
            catch (Exception e)
            {
                this.View.ShowError(String.Format(Messages.MsgInternalError, e.Message));
            }
            finally
            {
                this.Worker.EndWork();
            }
        }

        //TODO: Invoke from embedded presenter
        public void HandleDetailsPanelDoubleClick()
        {
            this.View.RemoveSelectionFromResultGrid();
        }

        private void SaveLayoutState()
        {
            this.UserSettingsService.SaveQueryLayoutState(this.View.GetLayoutState(), this.Query);
        }

        public override bool IsEqualTo(ITabPresenter presenter)
        {
            if (presenter is QueryPresenter)
            {
                var queryPresenter = presenter as QueryPresenter;
                return queryPresenter.Query.Guid == this.Query.Guid;
            }

            return false;
        }

        public void HandleWorkItemDoubleClicked()
        {
            this.MenuService.RunCommand(CommandsNames.OpenWorkItem);
        }

        public override void InvalidateHandledCommands()
        {
            base.InvalidateHandledCommands();
            this.EmbeddedWorkItemPresenter.InvalidateHandledCommands();
        }

        public override void DisableHandledCommands()
        {
            base.DisableHandledCommands();
            this.EmbeddedWorkItemPresenter.DisableHandledCommands();
        }

        public override void HandleActivation()
        {
            base.HandleActivation();
            this.EmbeddedWorkItemPresenter.HandleActivation();
        }

        public override void HandleDeactivation()
        {
            base.HandleDeactivation();
            this.EmbeddedWorkItemPresenter.HandleDeactivation();
        }

        public void HandleWorkItemDataChanged()
        {
            this.Tab.RefreshCaption();
            this.View.UpdateResultGridRows();
            this.InvalidateHandledCommands();
        }

        public override string GetCaption()
        {
            if (this.Query == null) return String.Empty;

            var format = "{0}";

            switch (this._queryState)
            {
                case QueryState.None:
                    format = Captions.TabQueryNormal; break;
                case QueryState.Refreshing:
                    format = Captions.TabQueryRunning; break;
                case QueryState.UnableToRefresh:
                    format = Captions.TabQueryUnableToRefresh; break;
            }

            return String.Format(format, this.Query.Name);
        }

        public override bool BelongsToServer(ServerDescriptor serverDescriptor)
        {
            return this.Query.Project.Server.Equals(serverDescriptor);
        }

        public bool IsAssociatedWith(ServerDescriptor descriptor)
        {
            return this.Query.Project.Server.Equals(descriptor);
        }
    }
}